package main

import (
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"os"
	"sync"
	"time"
)

// myWriter 自定义日志写入器结构体
// 功能：支持按日期自动切换日志文件，实现日志分割
// 字段说明：
// - mutex: 互斥锁，保证并发安全，防止多个goroutine同时修改文件指针
// - currentDate: 记录当前日志文件对应的日期（如 "2023-10-25"）
// - file: 当前正在写入的日志文件指针
type myWriter struct {
	mutex       sync.Mutex // 互斥锁，保证并发安全
	currentDate string     // 记录当前日志文件对应的日期（如 "20231025"）
	file        *os.File   // 当前正在写入的日志文件指针
}

// Write 方法实现 io.Writer 接口，用于写入日志数据
// 参数说明：
// - b: 要写入的字节数据
// 返回值：
// - n: 实际写入的字节数
// - err: 写入过程中发生的错误
// 功能：自动检测日期变化，在日期变更时切换到新的日志文件
func (m *myWriter) Write(b []byte) (n int, err error) {
	// 获取互斥锁，确保后续代码的线程安全（进入临界区）
	m.mutex.Lock()
	// 延迟释放互斥锁，函数返回时自动解锁（退出临界区）
	defer m.mutex.Unlock()

	// 获取当前日期，格式：年-月-日
	now := time.Now().Format("2006-01-02")

	// 如果日期没有变化，直接写入当前文件
	if m.currentDate == now {
		return m.file.Write(b)
	}

	// 如果已有日志文件打开，先关闭它（防止文件句柄泄漏）
	if m.file != nil {
		m.file.Close()
	}

	// 按日期生成日志文件名，格式：logs/2023-10-25.log
	name := fmt.Sprintf("logs/%s.log", now)

	// 创建或打开日志文件（追加模式，只写，权限0666）
	// os.O_CREATE: 文件不存在时创建
	// os.O_APPEND: 追加模式，写入内容添加到文件末尾
	// os.O_WRONLY: 只写模式
	file, _ := os.OpenFile(name, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)

	// 更新文件指针和当前日期
	m.file = file
	m.currentDate = now

	// 将日志数据写入新文件
	return file.Write(b)
}

// main 函数演示按日期分割日志的实现
// 通过自定义日志写入器实现自动日志分割功能
func main() {
	// 使用 zap 的 NewDevelopmentConfig 快速配置
	cfg := zap.NewDevelopmentConfig()
	// 替换时间格式化方式为 "年-月-日 时:分:秒"
	cfg.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")

	// 创建日志核心
	core := zapcore.NewCore(
		// 使用控制台格式编码器
		zapcore.NewConsoleEncoder(cfg.EncoderConfig),
		// 同时输出到控制台和文件
		// 使用自定义的日志写入器实现按日期分割
		zapcore.NewMultiWriteSyncer(os.Stdout, zapcore.AddSync(&myWriter{})),
		// 设置日志级别为Info（Info及以上级别的日志会被记录）
		zapcore.InfoLevel,
	)

	// 创建一个新的 zap 日志记录器实例
	logger := zap.New(
		core,            // 使用配置好的核心组件
		zap.AddCaller(), // 启用调用者信息记录（自动添加文件名和行号）
	)

	// 遍历测试：生成10条日志，每条间隔1秒
	// 如果测试跨越日期边界，会自动创建新的日志文件
	for i := 0; i < 10; i++ {
		// 使用Sugar日志器记录格式化日志
		logger.Sugar().Infof("this is %d log", i)
		// 暂停1秒，模拟真实场景中的日志间隔
		time.Sleep(time.Second)
	}
}
